package framework.storage;

import framework.config.FileConfig;
import framework.config.FileManagerConfig;
import framework.config.FileStorageHttpConfig;
import framework.config.S3Config;
import framework.exceptions.ConfigurationException;
import lombok.Getter;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 使用本地存储的文件存储默认实现
 */
public class FileManagerDefault implements FileManager, FileStorage, Iterable {
    private final Map<String, FileStorage> nameMap = new ConcurrentHashMap<>();
    private final Map<String, FileStorage> path0Map = new ConcurrentHashMap<>();
    @Getter
    protected FileStorage defaultStorage;
    @Getter
    private final FileManagerConfig config;

    public FileManagerDefault(FileManagerConfig config) {
        this.config = config;
        this.initialize();
        this.initDefault();
    }

    @SneakyThrows
    protected void initDefault() {
        FileStorage defaultStorage = findByName(this.config.getDefaultName());
        if (defaultStorage == null) {
            throw new ConfigurationException("Not config default instance");
        }
        this.defaultStorage = defaultStorage;
    }

    @SneakyThrows
    protected void initialize() {
        if (StringUtils.isBlank(this.config.getDefaultName())) {
            throw new ConfigurationException("default name not config");
        }
        if (this.config.getFiles() != null && this.config.getFiles().length > 0) {
            for (FileConfig fileConfig : this.config.getFiles()) {
                if (StringUtils.isBlank(fileConfig.getName())) {
                    throw new ConfigurationException("file storage config name not config");
                }
                //  当文件组没有配置压缩路径时，使用默认配置
                if (fileConfig.getCompressPathStarts() == null || fileConfig.getCompressPathStarts().length == 0) {
                    fileConfig.setCompressPathStarts(this.config.getCompressPathStarts());
                }
                //
                if (this.nameMap.containsKey(fileConfig.getName())) {
                    throw new ConfigurationException("file storage config " + fileConfig.getName() + " already exist");
                }
                //
                FileStorage storage = this.createFileStorageLocal(fileConfig);
                this.nameMap.put(fileConfig.getName(), storage);
                this.path0Put(fileConfig.getName(), fileConfig.getManagers(), storage);
            }
        }
        if (this.config.getS3s() != null && this.config.getS3s().length > 0) {
            for (S3Config s3Config : this.config.getS3s()) {
                if (StringUtils.isBlank(s3Config.getName())) {
                    throw new ConfigurationException("s3 storage config name not config");
                }
                //  当文件组没有配置压缩路径时，使用默认配置
                if (s3Config.getCompressPathStarts() == null || s3Config.getCompressPathStarts().length == 0) {
                    s3Config.setCompressPathStarts(this.config.getCompressPathStarts());
                }
                //
                if (this.nameMap.containsKey(s3Config.getName())) {
                    throw new ConfigurationException("s3 storage config " + s3Config.getName() + " already exist");
                }
                //
                FileStorage storage = this.createFileStorageS3(s3Config);
                this.nameMap.put(s3Config.getName(), storage);
                this.path0Put(s3Config.getName(), s3Config.getManagers(), storage);
            }
        }
        if (this.config.getHttps() != null && this.config.getHttps().length > 0) {
            for (FileStorageHttpConfig httpConfig : this.config.getHttps()) {
                if (StringUtils.isBlank(httpConfig.getName())) {
                    throw new ConfigurationException("http storage config name not config");
                }
                //  当文件组没有配置压缩路径时，使用默认配置
                if (httpConfig.getCompressPathStarts() == null || httpConfig.getCompressPathStarts().length == 0) {
                    httpConfig.setCompressPathStarts(this.config.getCompressPathStarts());
                }
                //
                if (this.nameMap.containsKey(httpConfig.getName())) {
                    throw new ConfigurationException("http storage config " + httpConfig.getName() + " already exist");
                }
                //
                FileStorage storage = this.createFileStorageHttp(httpConfig);
                this.nameMap.put(httpConfig.getName(), storage);
                this.path0Put(httpConfig.getName(), httpConfig.getManagers(), storage);
            }
        }
    }

    private void path0Put(String name, String[] managers, FileStorage storage) throws ConfigurationException {
        if (managers != null) {
            for (String path0 : managers) {
                FileStorage old = path0Map.get(path0);
                if (old != null) {
                    throw new ConfigurationException("Initialize file manager " + name + " failed, managers " + path0 + " duplication");
                }
                path0Map.put(path0, storage);
            }
        }
    }

    private FileStorage createFileStorageHttp(FileStorageHttpConfig httpConfig) {
        return new FileStorageHttp(httpConfig);
    }

    protected FileStorage createFileStorageLocal(FileConfig fileConfig) {
        return new FileStorageLocal(fileConfig);
    }

    protected FileStorage createFileStorageS3(S3Config s3Config) {
        return new FileStorageS3v1(s3Config);
    }

    public String[] compressionPathStarts() {
        return this.config.getCompressPathStarts();
    }

    @Override
    public long save(InputStream inputStream, String filePath, boolean compression) throws IOException {
        return findByPath(filePath).save(inputStream, filePath, compression);
    }

    @Override
    public InputStream getStream(String filePath, boolean compression) throws IOException {
        return findByPath(filePath).getStream(filePath, compression);
    }

    @Override
    public void delete(String filePath, boolean compression) throws IOException {
        findByPath(filePath).delete(filePath, compression);
    }

    @Override
    public long length(String filePath, boolean compression) throws IOException {
        return findByPath(filePath).length(filePath, compression);
    }

    public FileStorage findByPath(String path) {
        if (StringUtils.isBlank(path)) {
            throw new IllegalArgumentException("path not allow null");
        }
        //
        String segment = getFirstSegment(path);
        if (StringUtils.isBlank(segment)) {
            return defaultStorage;
        }
        //
        FileStorage storage = path0Map.get(segment);
        if (storage != null) {
            return storage;
        }
        //
        return defaultStorage;
    }

    private String getFirstSegment(String path) {
        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        // 去除首部斜杠
        int start = 1;
        // 查找第一个斜杠位置
        int end = path.indexOf('/', start);
        // 返回有效片段
        return end == -1 ? path.substring(start) : path.substring(start, end);
    }

    @Override
    public Iterator iterator() {
        return nameMap.values().iterator();
    }

    @Override
    public int size() {
        return nameMap.size();
    }

    @Override
    public Set<String> names() {
        return nameMap.keySet();
    }

    @Override
    public FileStorage findByName(String name) {
        return nameMap.get(name);
    }
}
